//! orm_join 相关处理

use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use syn::{parse_str, Type};

use super::struct_field_parse::{
    FieldConvertType, TinyOrmField, TinyOrmFieldData, TinyOrmJoinField,
};

/// 结构体join相关汇总信息
#[derive(Clone, Debug)]
pub(crate) struct TinyOrmJoinSumInfo {
    /// join 时的select字段
    pub(crate) fields: String,
    /// select时的join语句部分
    pub(crate) join: String,
    /// 生成的相关join方法
    pub(crate) token: TokenStream2,
    /// self对应join的字段
    pub(crate) join_self_fields: Vec<TokenStream2>,
}
/// 解析并实现join相关方法等
///
/// #主要功能：
///
/// 1. 生成select join部分sql///
/// 2. 生成select join表对应的fields
/// 返回主函数后，与表自身的fields合并为select查询的fields内容
/// 3.根据join字段，自动生成以下方法：
///    1、通过join字段的查询方法
///    orm_query_join_with_field_name(pool,join_field_type)
///    2、通过join字段的删除方法
///    orm_delete_join_with_field_name(pool,join_field_type)
///    3、通过join字段的更新方法
///    orm_update_join_with_field_name(pool,join_field_type)
pub(super) fn impl_orm_join(
    orm_field_list: &[TinyOrmFieldData],
    pk_self_tokens: &Vec<TokenStream2>,
    _table_name: &str,
) -> Option<TinyOrmJoinSumInfo> {
    // 提取join字段
    let join_fields = orm_field_list
        .iter()
        .filter_map(|orm_field_data| {
            if let TinyOrmFieldData::Join(pk_field) = orm_field_data {
                Some(pk_field)
            } else {
                None
            }
        })
        .collect::<Vec<_>>();
    if join_fields.is_empty() {
        return None;
    }
    // 生成select join部分sql
    // 返回主函数后，与表自身的fields合并为select查询的fields内容
    let join_sql = join_fields
        .iter()
        .map(|field| field.join_sql.clone())
        .collect::<Vec<_>>()
        .join(" ");
    // 生成select join表对应的fields
    let select_field_from_join_sql = join_fields
        .iter()
        .map(|field| field.select_field.clone())
        .collect::<Vec<_>>()
        .join(", ");

    // 根据join字段，自动生成以下方法：
    // 1、通过join字段的查询方法
    //    orm_query_join_with_field_name(pool,join_field_type)
    // 2、通过join字段的删除方法
    //    orm_delete_join_with_field_name(pool,join_field_type)
    // 3、通过join字段的更新方法
    //    orm_update_join_with_field_name(pool,join_field_type)

    // 生成查询函数相关ident
    // 查询函数名称
    // 去掉跳过生成方法的字段
    let join_fields_no_skip = join_fields
        .iter()
        .filter(|join_field| !join_field.is_skip_method)
        .collect::<Vec<_>>();
    let query_fn_names = join_fields_no_skip
        .iter()
        .map(|join_field| format_ident!("orm_query_join_with_{}", join_field.field.field_ident))
        .collect::<Vec<_>>();
    let query_fn_docs = join_fields_no_skip
        .iter()
        .map(|join_field| {
            format!(
                "orm_join自动实现:通过join字段获取记录-{}",
                join_field.field.field_ident
            )
        })
        .collect::<Vec<_>>();
    let query_link_id_fn_names = join_fields_no_skip
        .iter()
        .map(|join_field| {
            format_ident!(
                "orm_query_join_with_{}_{}",
                join_field.field.field_ident,
                join_field.link_id
            )
        })
        .collect::<Vec<_>>();
    let query_link_id_fn_docs = join_fields_no_skip
        .iter()
        .map(|join_field| {
            format!(
                "orm_join自动实现:通过join字段的{}获取记录-{}",
                join_field.link_id, join_field.field.field_ident
            )
        })
        .collect::<Vec<_>>();
    // 删除函数名称
    let delete_fn_names = join_fields_no_skip
        .iter()
        .map(|join_field| format_ident!("orm_delete_join_with_{}", join_field.field.field_ident))
        .collect::<Vec<_>>();
    let delete_fn_docs = join_fields_no_skip
        .iter()
        .map(|join_field| {
            format!(
                "orm_join自动实现:通过join字段删除记录-{}",
                join_field.field.field_ident
            )
        })
        .collect::<Vec<_>>();
    let delete_link_id_fn_names = join_fields_no_skip
        .iter()
        .map(|join_field| {
            format_ident!(
                "orm_delete_join_with_{}_{}",
                join_field.field.field_ident,
                join_field.link_id
            )
        })
        .collect::<Vec<_>>();
    let delete_link_id_fn_docs = join_fields_no_skip
        .iter()
        .map(|join_field| {
            format!(
                "orm_join自动实现:通过join字段{}删除记录-{}",
                join_field.link_id, join_field.field.field_ident
            )
        })
        .collect::<Vec<_>>();
    // 更新函数名称
    let update_fn_names = join_fields_no_skip
        .iter()
        .map(|join_field| format_ident!("orm_update_join_with_{}", join_field.field.field_ident))
        .collect::<Vec<_>>();
    let update_fn_docs = join_fields_no_skip
        .iter()
        .map(|join_field| {
            format!(
                "orm_join自动实现:更新当前记录的join字段值-{}",
                join_field.field.field_ident
            )
        })
        .collect::<Vec<_>>();
    // join where and update set sql
    let where_and_set_sql = join_fields_no_skip
        .iter()
        .map(|join_field| format!("{} = ?", join_field.field.field_db_name))
        .collect::<Vec<_>>();

    // join字段ident
    let join_ident = join_fields_no_skip
        .iter()
        .map(|join_field| join_field.field.field_ident.clone())
        .collect::<Vec<_>>();
    // bind的值
    let join_ident_bind_value = join_fields_no_skip
        .iter()
        .map(|join_field| {
            let link_id = format_ident!("{}", join_field.link_id);
            let obj = join_field.field.field_ident.clone();
            quote! {
                #obj.#link_id
            }
        })
        .collect::<Vec<_>>();
    // join字段type
    let join_type = join_fields_no_skip
        .iter()
        .map(|join_field| join_field.field.field_type.clone())
        .collect::<Vec<_>>();

    let join_self_fields_no_skip = join_fields_no_skip
        .iter()
        .map(
            |TinyOrmJoinField {
                 field:
                     TinyOrmField {
                         field_type_convert_type,
                         field_ident,
                         field_type,
                         ..
                     },
                 link_id,
                 ..
             }| {
                let link_id = format_ident!("{}", link_id);
                match field_type_convert_type {
                    FieldConvertType::Option => {
                        if let Type::Path(_) = field_type {
                            quote! { &self.#field_ident.as_ref().unwrap().#link_id }
                        } else {
                            quote! { self.#field_ident.unwrap().#link_id }
                        }
                    }
                    FieldConvertType::String => quote! { &self.#field_ident.#link_id },
                    FieldConvertType::None => quote! { &self.#field_ident.#link_id },
                }
            },
        )
        .collect::<Vec<_>>();
    // 生成主键相关ident
    let mut pk_ident = Vec::with_capacity(join_fields.len());
    for _ in 0..join_fields.len() {
        pk_ident.push(pk_self_tokens)
    }

    // 生成主键连接id类型
    let link_id_types = join_fields
        .iter()
        .map(|join_field| {
            parse_str::<Type>(&join_field.link_id_type).unwrap_or_else(|_| panic!("转换link_id_type为type,字段名称{}",
                             join_field.field.field_ident))
            
        })
        .collect::<Vec<_>>();
    // 生成orm_get_by_pk 方法

    let code = quote! {
        #(
            #[doc = #query_fn_docs]
            pub async fn #query_fn_names(pool: &TinyOrmDbPool,#join_ident:#join_type) -> AnyhowResult<Vec<Self>>{
                let sql = Self::DB_META.build_select_sql(#where_and_set_sql);
                let query = Self::db_query(&sql)
                    .bind(#join_ident_bind_value);
                Self::db_fetch_all(pool, query, Self::orm_row_map).await
                    .with_context(|| #query_fn_docs)
            }
            #[doc = #query_link_id_fn_docs]
            pub async fn #query_link_id_fn_names(pool: &TinyOrmDbPool,query_value:#link_id_types) -> AnyhowResult<Vec<Self>>{
                let sql = Self::DB_META.build_select_sql(#where_and_set_sql);
                let query = Self::db_query(&sql)
                    .bind(query_value);
                Self::db_fetch_all(pool, query, Self::orm_row_map).await
                    .with_context(|| #query_link_id_fn_docs)
            }
        )*

        #(
            #[doc = #delete_fn_docs]
            pub async fn #delete_fn_names(pool: &TinyOrmDbPool,#join_ident:#join_type) -> AnyhowResult<()>{
                let sql = Self::DB_META.build_delete_sql(#where_and_set_sql);
                let query = Self::db_query(&sql)
                    .bind(#join_ident_bind_value);
                Self::db_execute(pool, query).await
                    .with_context(|| #delete_fn_docs)?;
                Ok(())
            }
            #[doc = #delete_link_id_fn_docs]
            pub async fn #delete_link_id_fn_names(pool: &TinyOrmDbPool,query_value:#link_id_types) -> AnyhowResult<()>{
                let sql = Self::DB_META.build_delete_sql(#where_and_set_sql);
                let query = Self::db_query(&sql)
                    .bind(query_value);
                Self::db_execute(pool, query).await
                    .with_context(|| #delete_link_id_fn_docs)?;
                Ok(())
            }
        )*

        #(
            #[doc = #update_fn_docs]
            pub async fn #update_fn_names(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>{
                let sql = Self::DB_META.build_update_sql(#where_and_set_sql,Some(Self::DB_META.pk_where_sql));
                let query = Self::db_query(&sql)
                    .bind(#join_self_fields_no_skip)
                    #(.bind(#pk_ident))*;
                Self::db_execute(pool, query).await
                    .with_context(|| #update_fn_docs)?;
                Ok(())
            }
        )*
    };

    let join_self_fields = join_fields
        .iter()
        .map(
            |TinyOrmJoinField {
                 field:
                     TinyOrmField {
                         field_type_convert_type,
                         field_ident,
                         field_type,
                         ..
                     },
                 link_id,
                 ..
             }| {
                let link_id = format_ident!("{}", link_id);
                match field_type_convert_type {
                    FieldConvertType::Option => {
                        if let Type::Path(_) = field_type {
                            quote! { &self.#field_ident.as_ref().unwrap().#link_id }
                        } else {
                            quote! { self.#field_ident.unwrap().#link_id }
                        }
                    }
                    FieldConvertType::String => quote! { &self.#field_ident.#link_id },
                    FieldConvertType::None => quote! { &self.#field_ident.#link_id },
                }
            },
        )
        .collect::<Vec<_>>();
    //dbg!(&code);
    Some(TinyOrmJoinSumInfo {
        fields: select_field_from_join_sql,
        join: join_sql,
        join_self_fields,
        token: code,
    })
}
